///////////////////////////////////////////////////////////
// 
//  MyWindow.cpp
//
//  Wiicussion
//  HCI 575 Class project
//  Spring 2007
// 
//  Authors:  Jeremy Bennett
//            Adam Faeth
//            Michael Oren
//
///////////////////////////////////////////////////////////

// Local Includes
#include "stdafx.h"
#include "MyWindow.h"
#include "Horse.h"

#ifdef __APPLE_CC__
#define _stricmp strcasecmp
#endif

#if defined(USE_MIDI_FUNCTIONS)
#include "PlayMidiLib.h"
#endif

#if defined(_WIN32)
// HaptCtrl Includes
#include <HaptWiimote.h>
#endif

// SDL Includes
#include <sdl.h>
#include <SDL_opengl.h>

// System Includes

//                                         | Mote 1    | Mote 2     |
const unsigned int s_uiMidiInstruments[] = { 48, 35, 42, 45, 38, 49 };

MyWindow::MyWindow()
: _context(),
  _pSurface(NULL),
  _pJoystick(),
  _nJoystick(0),
  _nPlayer(0),
  _player(),
  _w(1280),
  _h(800),
  _bPlaying(),
  _pGame(NULL),
  _bQuit(false)
{
 #if defined(USE_MIDI_FUNCTIONS)
	if(StartMidiOutForiDev(0)==MIDI_OUT_ERROR){
	   ("Couldn't open Midi Out - perhaps it is already in use for another application.\n\n"
		"Press return to exit."
	   );
	  getchar();
	  exit(0);
	 }
#endif
}

MyWindow::~MyWindow()
{
	#if defined(USE_MIDI_FUNCTIONS)
	StopMidiOut();
	#endif
}

bool
MyWindow::init()
{
    // Initialize SDL (Note: video is required to start event loop)
	if ( SDL_Init(SDL_INIT_VIDEO|SDL_INIT_JOYSTICK) < 0 ) {
		printf("ERROR - Initialization Failed: %s\n",SDL_GetError());
		return false;
	}

    // Initialize GL
    if (!_initGL())       return false;

#if defined(USE_GLOVEPIE)
    // Initialize the Joysticks
    if (!_initJoystick()) return false;
#else
    // Initialize the Wiimotes
    if (!_initWiimote()) return false;
#endif

    // Configure the context
    if (!_context.configure()) return false;

    for ( int x=0; x<_nPlayer; x++ ) {
        _player[x].setUIPosition( 0.0f,   _h-128.0f*(x+1),
                                  256.0f, 128.0f );
        _player[x].calibrateInstuments(0,0,_w,_h,0,5);
    }

    _stage.loadStage();
    
    return true;
}

bool
MyWindow::fini()
{
    SDL_Quit();

#if !defined(USE_GLOVEPIE)
    HaptWiimote::disconnect();
#endif

    return true;
}

bool 
MyWindow::_initGL()
{
    printf("***** Initializing OpenGL Window ***** \n");

    int bpp = SDL_GetVideoInfo()->vfmt->BitsPerPixel;
    unsigned int myFlags = SDL_OPENGL;
                       //| SDL_FULLSCREEN;


    SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
    SDL_GL_SetAttribute( SDL_GL_ACCELERATED_VISUAL, 1 );
    SDL_GL_SetAttribute( SDL_GL_SWAP_CONTROL, 0 );

    _pSurface = SDL_SetVideoMode( _w, _h, bpp, myFlags );
    if ( _pSurface == NULL ) {
		printf("ERROR - SetVideoMode Failed: %s\n",SDL_GetError());
		return false;
	} 

    setViewPort(0,0,_w,_h);

    glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
    //glBlendFunc(GL_SRC_ALPHA,GL_DST_ALPHA);
    glEnable(GL_BLEND);

    printf("***** Initializing OpenGL Complete ***** \n");
    
    printf("\n");
	printf( "Vendor     : %s\n", glGetString( GL_VENDOR ) );
	printf( "Renderer   : %s\n", glGetString( GL_RENDERER ) );
	printf( "Version    : %s\n", glGetString( GL_VERSION ) );
	printf( "Extensions : %s\n", glGetString( GL_EXTENSIONS ) );
	printf("\n");

    SDL_WM_SetCaption( "Wiicussion", "Fasten Your Wrist Straps" );


    return true;
}


bool 
MyWindow::_initJoystick()
{
#if defined(USE_GLOVEPIE)
    // Local variables
    int i = 0;
	
    printf("***** Initializing Joysticks ***** \n");
    // Find and store all the PPJoy virtual joysticks
    while ( i < SDL_NumJoysticks() && _nJoystick < 8 ) {
        if ( strncmp(SDL_JoystickName(i),"PPJoy Virtual joystick",22) == 0) {
            _pJoystick[_nJoystick] = SDL_JoystickOpen(i);
            if ( !_pJoystick[_nJoystick] ) return false;
                _nJoystick++;
        	    printf("Joystick  : %s\n", SDL_JoystickName(i) ? SDL_JoystickName(i) : "Unknown");
                printf("  Axes    : %d\n", SDL_JoystickNumAxes(_pJoystick[_nJoystick - 1]));
                printf("  Hats    : %d\n", SDL_JoystickNumHats(_pJoystick[_nJoystick - 1]));
                printf("  Balls   : %d\n", SDL_JoystickNumBalls(_pJoystick[_nJoystick -1]));
                printf("  Buttons : %d\n", SDL_JoystickNumButtons(_pJoystick[_nJoystick -1]));
        }
         
        i++;
    }

    // Loop through all the avaible joysticks create as many players
    // as possible using two joysticks per player.
    i = 0;
    while ( _nJoystick-i >= 2 && _nPlayer < 4 ) {
        _player[_nPlayer].setJoystick( _pJoystick[i], _pJoystick[i+1] );
        _player[_nPlayer].setID(_nPlayer);
        _nPlayer += 1;
        i += 2;  // Two Joysticks per each player
    }

    printf("***** Initializing Joysticks Complete ***** \n");
#endif

    return true;
}

bool 
MyWindow::_initWiimote()
{

#if defined(_WIN32)
    HaptWiimote::connect();
    
    HaptWiimote *pMote1 = NULL,
                *pMote2 = NULL;
    int mote, nMote=HaptWiimote::numWiimotes();
    for ( mote=0; mote<nMote; mote+=2  ) {
        // Acquire and initialize first wiimote
        pMote1 = HaptWiimote::wiimote(mote);
        if (pMote1) {
            pMote1->enableThread(true);
            pMote1->setRumble(false);
        }
        // Acquire and initialize second wiimote
        pMote2 = HaptWiimote::wiimote(mote+1);
        if (pMote2) {
            pMote2->enableThread(true);
            pMote2->setRumble(false);
        }
        // Set the Wiimotes on the player and inc the player count
        _player[_nPlayer].setWiimote( pMote1, pMote2 );
        _nPlayer++;
    }
#endif

    return true;
}

bool 
MyWindow::execute( int argv, char* argc[] )
{
    _bQuit = false;
    unsigned int uiStop = SDL_GetTicks() + 8000;
    
    // loop through the warning screen
    while( SDL_GetTicks() < uiStop && !_bQuit) {
        if(!_preFrame())        { _bQuit = true; break; }
        if(!_renderWarning())   { _bQuit = true; break; }
    }
    _bQuit = false;
    
    while  ( !_bQuit ) {
        if (!_preFrame())  break;
        if (!_render())    break;
        if (!_postFrame()) break;
    }

    return true;
}


bool
MyWindow::_preFrame()
{
    // Update the player state
    for ( int x=0; x<_nPlayer; x++ ) {
       _player[x].preFrame();
    }

    // Forward to the game if present
    if( _pGame ) {
        if( _pGame->handleEvents() ) {
            return true;
        }
        else {
            // if the game didn't handle events, delete it
            delete _pGame;
            _pGame = NULL;
        }
    }

    
    // Otherwise, let the window handle the event
    if (!_handleEvents() )  {
        return false;
    }
    return true;
}


bool
MyWindow::_postFrame()
{
    bool bRes = true;
    if(_pGame)
        bRes &= _pGame->postFrame();
    return bRes;
}


bool
MyWindow::_render()
{
	glClearColor( 0.0, 0.0, 0.0, 1.0 );
	glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    _renderBackground();
	_renderMenu();
    
    _stage.renderStage(&_context);
    
    if(_pGame) _pGame->renderGame(&_context);

    for ( int x=0; x<_nPlayer; x++ ) {
        _player[x].renderPlayer(&_context);
        _player[x].renderPointer(&_context);
    }

    SDL_GL_SwapBuffers( );

    return true;
}

bool 
MyWindow::_renderBackground()
{
    MyImage *pImage1 = (MyImage*)_context.texture(WINDOW_BACKGROUND_TEX);

	if(!pImage1->isLoaded())
		return true;

    pImage1->bindTexture();
    pImage1->enableTexture();

    glBegin(GL_TRIANGLE_STRIP);
    // Vertex 0
    glTexCoord2f(0.0f,0.0f);
    glVertex3f(0.0f,0.0f,0.0f);
    // Vertex 1
    glTexCoord2f(0.0f,1.0f);
    glVertex3f(0.0f,_h,0.0f);
    // Vertex 2
    glTexCoord2f(1.0f,0.0f);
    glVertex3f(_w,0.0f,0.0f);
    // Vertex 3
    glTexCoord2f(1.0f,1.0f);
    glVertex3f(_w,_h,0.0f);
    glEnd();

    pImage1->disableTexture();

    return true;
}

bool 
MyWindow::_renderMenu()
{
	if(_pGame && _pGame->isActive())
		return true;

    MyImage *pImage1 = (MyImage*)_context.texture(WINDOW_BUTTON_TEX);

    pImage1->bindTexture();
    pImage1->enableTexture();

	_context.renderRect(_w/2.0f-128.0f,_h/2.0f-32.0f,256, 64);
	_context.renderText("Start Game",_w/2.0f-40.0f,_h/2.0f-5.0f);

    pImage1->disableTexture();

    return true;
}

bool
MyWindow::_renderWarning()
{
    glClearColor( 0.0, 0.0, 0.0, 1.0 );
	glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
    
    MyImage *pWarnImg = (MyImage*)_context.texture(WINDOW_WARNING_TEX);

	if(!pWarnImg->isLoaded()){
//		printf("No Warning!\n");
		return true;
	}
    
    pWarnImg->bindTexture();
    pWarnImg->enableTexture();
    
    glBegin(GL_TRIANGLE_STRIP);
    
    // Vertex 0
    glTexCoord2f(0.0f,0.0f);
    glVertex3f(_w/2.0f-512.0f,_h/2.0f-256.0f,0.0f);
    // Vertex 1
    glTexCoord2f(0.0f,1.0f);
    glVertex3f(_w/2.0f-512.0f,_h/2.0f+256.0f,0.0f);
    // Vertex 2
    glTexCoord2f(1.0f,0.0f);
    glVertex3f(_w/2.0f+512.0f,_h/2.0f-256.0f,0.0f);
    // Vertex 3
    glTexCoord2f(1.0f,1.0f);
    glVertex3f(_w/2.0f+512.0f,_h/2.0f+256.0f,0.0f);
    glEnd();
    
    pWarnImg->disableTexture();
    
    
    SDL_GL_SwapBuffers( );
    
    return true;
}

bool
MyWindow::_handleEvents()
{
    // Handle all event that occur while in menu mode:

     _updateState();

    SDL_Joystick* pJoy = NULL; 
	bool bSustain=false;

    SDL_Event   event;

    while ( SDL_PollEvent(&event) ) {
		int iDegree=-1;
	    switch (event.type) {
            case SDL_JOYBUTTONDOWN:
			  printf("Joystick %d button %d down\n",event.jbutton.which,event.jbutton.button);
              pJoy = SDL_JoystickOpen(event.jbutton.which);
			  #if defined(USE_MIDI_FUNCTIONS)
			  StopAllNotesPlayed(127);
			  #endif
              for ( int x=0; x<_nPlayer; x++ ) {
#if defined(USE_GLOVEPIE)
                  if ( pJoy == _player[x].joystick()) {
					  _player[x].setButton(int(event.jbutton.button),true);
					  int iDegree2 = -1;
					  for(int s = 4; s < 6; s++){
						  if(_player[x].getButton(s)){
							  switch(s){
								case 4: iDegree2 = 48; break;
								case 5: iDegree2 = 35; break;
								default: iDegree2 = 42;
							  }
						  }
					  }
					  if(iDegree2 == -1)
						  iDegree2 = 42;
					  _player[x].setInstrument(0, iDegree2-35);

					  if(_player[x].getButton(11)){
						  bool played = false;
						  for(int s = 4; s < 6; s++){
							  if(_player[x].getButton(s)){
								printf("%d\n",s);
								switch(s){
									case 4: iDegree = 48; played=true;
											  #if defined(USE_MIDI_FUNCTIONS)
											  if(iDegree>=0){
												PlayNonMelodicPercussion(FIRST_GM_NON_MELOD_PERC+iDegree,127);
											  }
											  #endif
											  break;
									case 5: iDegree = 35; played=true;
											  #if defined(USE_MIDI_FUNCTIONS)
											  if(iDegree>=0){
												PlayNonMelodicPercussion(FIRST_GM_NON_MELOD_PERC+iDegree,127);
											  }
											  #endif
											  break;
								}
							  }
							 }
						  if(!played){
							  iDegree = 42;
							   #if defined(USE_MIDI_FUNCTIONS)
							  if(iDegree>=0){
								PlayNonMelodicPercussion(FIRST_GM_NON_MELOD_PERC+iDegree,127);
							  }
							  #endif
						  }
					  }
				  }
				  else if ( pJoy == _player[x].joystick2()) {
                      //_player[x].toggleActive();
					  _player[x].setButton2(int(event.jbutton.button),true);
					  iDegree=-1;
					  int iDegree2 = -1;
					  for(int s = 4; s < 6; s++){
						  if(_player[x].getButton2(s)){
							  switch(s){
								case 4: iDegree2 = 45; break;
								case 5: iDegree2 = 38; break;
								default: iDegree2 = 49;
							  }
						  }
					  }
					  if(iDegree2 == -1)
						  iDegree2 = 49;
					  _player[x].setInstrument(1, iDegree2-35);
					  if(_player[x].getButton2(11)){
						  bool played = false;
						  for(int s = 4; s < 6; s++){
							  if(_player[x].getButton2(s)){
								  switch(s){
									case 4: iDegree = 45; played=true;
											  #if defined(USE_MIDI_FUNCTIONS)
											  if(iDegree>=0){
												PlayNonMelodicPercussion(FIRST_GM_NON_MELOD_PERC+iDegree,127);
											  }
											  #endif
											  break;
									case 5: iDegree = 38; played=true;
											  #if defined(USE_MIDI_FUNCTIONS)
											  if(iDegree>=0){
												PlayNonMelodicPercussion(FIRST_GM_NON_MELOD_PERC+iDegree,127);
											  }
											  #endif
											  break;
								}
							  }
							 }
						  if(!played){
							  iDegree = 49;
							   #if defined(USE_MIDI_FUNCTIONS)
							  if(iDegree>=0){
								PlayNonMelodicPercussion(FIRST_GM_NON_MELOD_PERC+iDegree,127);
							  }
							  #endif
						  }
					  }
				  }
#endif
			  }
			break;
		    case SDL_JOYBUTTONUP:
			  //printf("Joystick %d button %d up\n",event.jbutton.which,event.jbutton.button);
			   pJoy = SDL_JoystickOpen(event.jbutton.which);
			   
			   #if defined(USE_MIDI_FUNCTIONS)
			   if( int(event.jbutton.button) == 11 && !bSustain)
				   StopAllNotesPlayed(127);
			   #endif

			   for ( int x=0; x < _nPlayer; x++ ) {
#if defined(USE_GLOVEPIE)
				    if ( pJoy == _player[x].joystick()) {
                          //_player[x].toggleActive();
					      _player[x].setButton(int(event.jbutton.button),false);
					      int iDegree2 = -1;
					      for(int s = 4; s < 6; s++){
						      if(_player[x].getButton(s)){
							      switch(s){
								    case 4: iDegree2 = 48; break;
								    case 5: iDegree2 = 35; break;
								    default: iDegree2 = 42;
							      }
						      }
					      }
					      if(iDegree2 == -1)
						      iDegree2 = 42;
					      _player[x].setInstrument(0, iDegree2-35);
				    }
				    if ( pJoy == _player[x].joystick2()) {
                          //_player[x].toggleActive();
					      _player[x].setButton2(int(event.jbutton.button),false);
					      int iDegree2 = -1;
					      for(int s = 4; s < 6; s++){
						      if(_player[x].getButton2(s)){
							      switch(s){
								    case 4: iDegree2 = 45; break;
								    case 5: iDegree2 = 38; break;
								    default: iDegree2 = 49;
							      }
						      }
					      }
					      if(iDegree2 == -1)
						      iDegree2 = 49;
					      _player[x].setInstrument(1, iDegree2-35);
				    }
#endif
			   }
			break;
            case SDL_KEYDOWN: 
                if (event.key.keysym.sym == SDLK_g) {
                    // TOXIC:  need to check for an old game eventually
					if(!_pGame){
						_pGame = new Horse(0, 0, _w, _h, _nPlayer, 7, _player);
						_pGame->activate();
					}
					else{
						delete _pGame;
						_pGame = new Horse(0, 0, _w, _h, _nPlayer, 7, _player);
						_pGame->activate();
					}
                    break;
                }
                if ( event.key.keysym.sym != SDLK_ESCAPE ) {
                    break;  // Allow escape key to be used to quit
                }
            case SDL_QUIT:
			    _bQuit = 1;
			break;
		    default:
			break;
        }
    }

    return true;
}

bool
MyWindow::_updateState()
{
    for ( int player = 0; player<_nPlayer; player++ ) {
        _updatePlaying(player,0); // Test Mote 1
        _updatePlaying(player,1); // Test Mote 2
    }

	float x, y, x2, y2;
	x = y = x2 = y2 = 0;
	int iMote, iMote2;
	iMote = iMote2 = -1;

	if (_player[0].isButtonPressed(0,WIIMOTE_BUTTON_A)){
		_player[0].getPosition(0, x, y);
		iMote = 0;
	}
	if(_player[0].isButtonPressed(1,WIIMOTE_BUTTON_A)){
		_player[0].getPosition(1, x, y);
		iMote = 1;
	}

	if(iMote != -1 && x >= _w/2.0f-128.0f && x <= _w/2.0f+128.0f && y >= _h/2.0f-32.0f && y <= _h/2.0f+32.0f){
		if(_player[0].getRumble(iMote))
			_player[0].setRumble(iMote, false);
		if(!_pGame){
			_pGame = new Horse(0, 0, _w, _h, _nPlayer, 7, _player);
			_pGame->activate();
		}
		else{
			delete _pGame;
			_pGame = new Horse(0, 0, _w, _h, _nPlayer, 7, _player);
			_pGame->activate();
		}
	}
	else if(iMote2 != -1 && x2 >= _w/2.0f-128.0f && x2 <= _w/2.0f+128.0f && y2 >= _h/2.0f-32.0f && y2 <= _h/2.0f+32.0f){
		if(_player[0].getRumble(iMote2))
			_player[0].setRumble(iMote2, false);
		if(!_pGame){
			_pGame = new Horse(0, 0, _w, _h, _nPlayer, 7, _player);
			_pGame->activate();
		}
		else{
			delete _pGame;
			_pGame = new Horse(0, 0, _w, _h, _nPlayer, 7, _player);
			_pGame->activate();
		}
	}
	else{
		_player[0].getPosition(0, x, y);
		if(x >= _w/2.0f-128.0f && x <= _w/2.0f+128.0f && y >= _h/2.0f-32.0f && y <= _h/2.0f+32.0f){
			if(!_player[0].getRumble(0))
				_player[0].setRumble(0, true);
		}
		else{
			if(_player[0].getRumble(0))
				_player[0].setRumble(0, false);
		}
		_player[0].getPosition(1, x, y);
		if(x >= _w/2.0f-128.0f && x <= _w/2.0f+128.0f && y >= _h/2.0f-32.0f && y <= _h/2.0f+32.0f){
			if(!_player[0].getRumble(1))
				_player[0].setRumble(1, true);
		}
		else{
			if(_player[0].getRumble(1))
				_player[0].setRumble(1, false);
		}
	}
	
	return true;
}

bool 
MyWindow::_updatePlaying( int player, int iMote )
{
    Player* p = &_player[player];
    bool bMatched = false;
    if ( p->isPlayingNote(iMote) ) {
        if ( _bPlaying[player][iMote] == false ) {
            int intsr = p->instrument(iMote);
            #if defined(USE_MIDI_FUNCTIONS)
            PlayNonMelodicPercussion(FIRST_GM_NON_MELOD_PERC+s_uiMidiInstruments[p->instrument(iMote)],127);
            #endif
            _bPlaying[player][iMote] = true;
        }
    } else {
        StopAllNotesPlayed(127);
        _bPlaying[player][iMote] = false;
    }
    

    return true;
}

bool 
MyWindow::setViewPort( int x, int y, int w, int h )
{
    glViewport( x, y, w, h );
    glMatrixMode( GL_PROJECTION );
	glLoadIdentity( );

	glOrtho( x, w, y, h, -20.0, 20.0 );

	glMatrixMode( GL_MODELVIEW );
	glLoadIdentity( );

    return true;
}

